if (!com) var com = {}
if (!com.logicpartners) com.logicpartners = {}

function precisionNumber(val, n = 2) {
  if (isNaN(val)) return val
  if (n == 0) return Math.round(val)
  return Math.round(val * 10 * n) / (10 * n)
}

// http://stackoverflow.com/a/5932203/697477
// 在canvas元素上添加原型方法，处理鼠标移动位置
HTMLCanvasElement.prototype.RelativeMouse = function (event) {
  var scale = this.scale ? this.scale : 1
  var totalOffsetX = 0
  var totalOffsetY = 0
  var canvasX = 0
  var canvasY = 0
  var currentElement = this
  do {
    totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft
    totalOffsetY += currentElement.offsetTop - currentElement.scrollTop
  } while ((currentElement = currentElement.offsetParent))

  canvasX = precisionNumber((event.clientX - totalOffsetX) / scale)
  canvasY = precisionNumber((event.clientY - totalOffsetY) / scale)

  return { x: canvasX, y: canvasY }
}

// From http://stackoverflow.com/a/4577326/697477
// 画出虚线
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype
if (CP && CP.lineTo) {
  CP.dashedLine = function (x, y, x2, y2, dashArray) {
    if (!dashArray) dashArray = [10, 5]
    if (dashLength == 0) dashLength = 0.001 // Hack for Safari
    var dashCount = dashArray.length
    this.moveTo(x, y)
    var dx = x2 - x,
      dy = y2 - y
    var slope = dx ? dy / dx : 1e15
    var distRemaining = Math.sqrt(dx * dx + dy * dy)
    var dashIndex = 0,
      draw = true
    while (distRemaining >= 0.1) {
      var dashLength = dashArray[dashIndex++ % dashCount]
      if (dashLength > distRemaining) dashLength = distRemaining
      var xStep = Math.sqrt((dashLength * dashLength) / (1 + slope * slope))
      if (dx < 0) xStep = -xStep
      x += xStep
      y += slope * xStep
      this[draw ? 'lineTo' : 'moveTo'](x, y)
      distRemaining -= dashLength
      draw = !draw
    }
    // Ensure that the last segment is closed for proper stroking
    this.moveTo(0, 0)
  }

  CP.dashedStroke = function (x, y, x2, y2, dashArray) {
    this.beginPath()
    this.dashedLine(x, y, x2, y, dashArray)
    this.dashedLine(x2, y, x2, y2, dashArray)
    this.dashedLine(x2, y2, x, y2, dashArray)
    this.dashedLine(x, y, x, y2, dashArray)
    this.stroke()
  }
}
// init
com.logicpartners.labelDesigner = function (canvasid, width, height, scale) {
  this.canvas = document.getElementById(canvasid)
  this.canvasContainer = $('#canvasContainer')
  this.canvasElement = $(this.canvas)
  this.propertyInspector = new com.logicpartners.propertyInspector(this, this.canvas) // this:window.canvasDesigner
  this.toolbar = new com.logicpartners.toolsWindow(this, this.canvas)
  // this.labelInspector = new com.logicpartners.labelInspector(this, this.canvas)

  this.drawingContext = this.canvas.getContext('2d')
  this.elements = [] // canvas 画板上canvas
  this.currentLayer = 0 // canvas画板数据数量
  this.activeElement = null // 当前激活元素
  this.activeTool = null

  this.newObject = null
  this.dragStartPosition = { x: 0, y: 0 }
  this.dragStartTime = 0
  this.dragLastPosition = { x: 0, y: 0 }
  this.dragElementOffset = { x: 0, y: 0 }
  this.dragAction = 0
  this.dragging = false

  var self = this

  // 操作尺寸
  $('#labelWidth')
    .on('blur', function () {
      self.updateLabelSize()
    })
    .on('keypress', function (e) {
      if (e.which == 13) {
        e.preventDefault()
        self.updateLabelSize()
      }
    })
  $('#labelHeight')
    .on('blur', function () {
      self.updateLabelSize()
    })
    .on('keypress', function (e) {
      if (e.which == 13) {
        e.preventDefault()
        self.updateLabelSize()
      }
    })
  $('#labelScale')
    .on('blur', function () {
      self.updateLabelSize()
    })
    .on('keypress', function (e) {
      if (e.which == 13) {
        // 回车
        e.preventDefault()
        self.updateLabelSize()
      }
    })

  // 打印设计
  $('#openWin').on('click', function () {
    window.open(window.location.href)
  })
  // 保存数据
  $('#saveData').on('click', function () {
    // console.log(self.elements, 'self.elements')
    labelData = {
      labelWidth: self.labelWidth,
      labelHeight: self.labelHeight,
      labelScale: self.labelScale,
      elements: self.elements, // 回显的数据canvas图
    }
    $.ajax({
      url: updateUrl,
      type: 'post',
      headers: { Authorization: `Bearer ${token}` },
      data: JSON.stringify({
        id: id,
        content: JSON.stringify(labelData),
        size: `${self.labelWidth}*${self.labelHeight}`,
        zpl: self.generateZPL(),
      }),
      processData: false,
      contentType: 'application/json',
      success: function (r) {
        if (r.status) alert('保存成功')
        else alert('保存失败:' + r.message)
      },
      error: function (e) {
        alert('保存失败')
        console.log(e)
      },
    })
  })

  // 如果接口返回数据 数据回显操作
  if (labelData.elements) {
    // console.log(labelData.elements, 'labelData.elements')
    labelData.elements.forEach((i) => {
      if (!i) return
      // 回显具体canvas数据 ---根据类型进行回显
      var objectConst = new com.logicpartners.designerTools[i.toolName]()
      var t = new objectConst.object(0, 0, 1, 1)
      self.elements.push(Object.assign(t, i))
    })
    // 当前画板数据量个数
    self.currentLayer = self.elements.length
  }

  // 画板尺寸修改
  this.updateLabelSize = function (w, h, s) {
    if (!w) w = parseInt($('#labelWidth').val())
    if (!h) h = parseInt($('#labelHeight').val())
    if (!s) s = parseInt($('#labelScale').val())
    this.labelScale = s > 0 ? s : 1
    this.labelWidth = w
    this.labelHeight = h
    this.canvasContainer.width(w * this.labelScale).height(h * this.labelScale)
    // 设置尺寸缩放
    this.canvasElement
      .prop('width', w * this.labelScale)
      .prop('height', h * this.labelScale)
      .prop('scale', this.labelScale)
    this.drawingContext.scale(this.labelScale, this.labelScale)
    $('#labelWidth').val(w)
    $('#labelHeight').val(h)
    $('#labelScale').val(s)
    this.updateCanvas()
  }
  // self: com.logicpartners.labelDesigner
  //newObjectController: 当前左侧图例
  // 定义canvas 元素事件
  this.canvasElement
    .on('click', function () {
      self.setActiveElement()
    })
    .on('mousedown', function () {
      self.dragStartPosition = self.canvas.RelativeMouse(event)
      self.dragLastPosition = self.dragStartPosition
      if (self.newObject) {
        // Create new object.
        self.addObject(new self.newObject(self.dragStartPosition.x, self.dragStartPosition.y, 1, 1))
        self.dragAction = 8
        self.activeElement = self.elements[self.currentLayer - 1]
        self.newObjectController.button.removeClass('designerToolbarButtonActive')
        self.newObject = null
        self.newObjectController = null
      } else {
        self.dragAction = 0
        self.setActiveElement()
        if (self.activeElement) {
          self.dragElementOffset = {
            x: self.activeElement.x - self.dragStartPosition.x,
            y: self.activeElement.y - self.dragStartPosition.y,
          }
          self.setActiveHandle(self.dragStartPosition)
        }
      }
      self.dragging = true
    })
    .on('mouseup', function () {
      self.dragging = false
    })
    .on('mouseout', function () {
      self.dragging = false
    })
    .on('mousemove', function () {
      //激活对象并且是在拖动
      if (self.dragging && self.activeElement) {
        var coords = self.canvas.RelativeMouse(event)
        switch (self.dragAction) {
          case 0:
            //移动位置
            self.move(coords.x + self.dragElementOffset.x, coords.y + self.dragElementOffset.y)
            break
          default:
            //改变尺寸
            self.resize(coords.x - self.dragLastPosition.x, coords.y - self.dragLastPosition.y, self.dragAction)
            break
        }
        self.updateCanvas()
        self.dragLastPosition = coords
      } else if (self.newObject != null) {
        self.canvasElement.css({ cursor: 'crosshair' })
      } else if (self.activeElement) {
        var coords = self.canvas.RelativeMouse(event)
        // If cursor is within range of edge, show resize handles
        var location = self.getHandle(coords)
        var style = 'default'
        switch (location) {
          case 0:
            style = 'default'
            break
          case 1:
            style = 'nw-resize'
            break
          case 2:
            style = 'n-resize'
            break
          case 3:
            style = 'ne-resize'
            break
          case 4:
            style = 'w-resize'
            break
          case 5:
            style = 'e-resize'
            break
          case 6:
            style = 'sw-resize'
            break
          case 7:
            style = 's-resize'
            break
          case 8:
            style = 'se-resize'
            break
        }
        self.canvasElement.css({ cursor: style })
      }
    })
    .on('keydown', function (event) {
      event = event || window.event
      var handled = false
      switch (event.keyCode) {
        case 37: // Left arrow
          if (self.activeElement) self.activeElement.x -= 1
          handled = true
          break
        case 38: // Up arrow
          if (self.activeElement) self.activeElement.y -= 1
          handled = true
          break
        case 39: // Right arrow
          if (self.activeElement) self.activeElement.x += 1
          handled = true
          break
        case 40: // Down arrow
          if (self.activeElement) self.activeElement.y += 1
          handled = true
          break
        case 46:
          if (self.activeElement) {
            self.deleteActiveElement()
            handled = true
          }
          break
        case 80:
          if (event.ctrlKey) {
            self.generateZPL()
            handled = true
          }
          break
      }
      if (handled) {
        self.updateCanvas()
        event.preventDefault()
        event.stopPropagation()
      }
    })

  // 添加tool数据
  this.addObject = function (object) {
    object.layerIndex = this.currentLayer
    this.elements[this.currentLayer] = object
    this.activeElement = this.elements[this.currentLayer]
    this.currentLayer++
    this.updateCanvas()
  }

  this.deleteActiveElement = function () {
    if (this.activeElement) {
      for (var i = 0; i < this.currentLayer; i++) {
        if (this.elements[i] && this.elements[i] == this.activeElement) {
          this.elements[i] = null
          this.activeElement = null
        }
      }
    }
  }

  //  设置选中的canvas图样式
  this.setActiveElement = function () {
    var coordinates = this.canvas.RelativeMouse(event) // 获取位置

    if (!this.activeElement || this.getHandle(coordinates) == 0) {
      // 选中的或者有缩放？
      this.activeElement = null
      for (var i = this.currentLayer - 1; i >= 0; i--) {
        if (this.elements[i] && this.elements[i].hitTest(coordinates)) {
          this.activeElement = this.elements[i]
          break
        }
      }
    }
    this.updateCanvas()
  }

  /**
   * Parameters: Coordinates on canvas.
   *
   * Returns: 0 if not on resize zone.
   *          1 top left     2 top     3 top right
   *          4 left                   5 right
   *          6 bottom left  7 bottom  8 bottom right
   */
  this.setActiveHandle = function (coords) {
    this.dragAction = this.getHandle(coords)
  }
  // 判断鼠标点击的位置
  this.getHandle = function (coords) {
    var result = 0

    var widthCanChange = !(this.activeElement.readonly && this.activeElement.readonly.includes('width'))
    var heightCanChange = !(this.activeElement.readonly && this.activeElement.readonly.includes('height'))

    var tmpWidth = 5 / this.labelScale
    var leftEdge =
      widthCanChange && coords.x > this.activeElement.x - tmpWidth && coords.x < this.activeElement.x + tmpWidth
    var rightEdge =
      widthCanChange &&
      coords.x > this.activeElement.x + this.activeElement.getWidth() - tmpWidth &&
      coords.x < this.activeElement.x + this.activeElement.getWidth() + tmpWidth
    var topEdge =
      heightCanChange && coords.y > this.activeElement.y - tmpWidth && coords.y < this.activeElement.y + tmpWidth
    var bottomEdge =
      heightCanChange &&
      coords.y > this.activeElement.y + this.activeElement.getHeight() - tmpWidth &&
      coords.y < this.activeElement.y + this.activeElement.getHeight() + tmpWidth

    var verticalHit =
      coords.y > this.activeElement.y && coords.y < this.activeElement.y + this.activeElement.getHeight()
    var horizontalHit =
      coords.x > this.activeElement.x && coords.x < this.activeElement.x + this.activeElement.getWidth()

    if (leftEdge && topEdge) result = 1
    else if (rightEdge && topEdge) result = 3
    else if (leftEdge && bottomEdge) result = 6
    else if (rightEdge && bottomEdge) result = 8
    else if (topEdge && horizontalHit) result = 2
    else if (leftEdge && verticalHit) result = 4
    else if (rightEdge && verticalHit) result = 5
    else if (bottomEdge && horizontalHit) result = 7

    return result
  }

  this.move = function (x, y) {
    this.activeElement.x = x
    this.activeElement.y = y
  }

  //拖动边框的处理
  this.resize = function (xchange, ychange) {
    switch (this.dragAction) {
      case 1: // Top Left
        this.activeElement.x += xchange
        this.activeElement.y += ychange
        this.activeElement.setWidth(this.activeElement.getWidth() - xchange)
        this.activeElement.setHeight(this.activeElement.getHeight() - ychange)
        break
      case 2: // Top
        this.activeElement.y += ychange
        this.activeElement.setHeight(this.activeElement.getHeight() - ychange)
        break
      case 3: // Top Right
        this.activeElement.setWidth(this.activeElement.getWidth() + xchange)
        this.activeElement.y += ychange
        this.activeElement.setHeight(this.activeElement.getHeight() - ychange)
        break
      case 4: // Left
        this.activeElement.x += xchange
        this.activeElement.setWidth(this.activeElement.getWidth() - xchange)
        break
      case 5: // Right
        this.activeElement.setWidth(this.activeElement.getWidth() + xchange)
        break
      case 6: // Bottom Left
        this.activeElement.x += xchange
        this.activeElement.setWidth(this.activeElement.getWidth() - xchange)
        this.activeElement.setHeight(this.activeElement.getHeight() + ychange)
        break
      case 7: // Bottom
        this.activeElement.setHeight(this.activeElement.getHeight() + ychange)
        break
      case 8: // Bottom Right
        this.activeElement.setWidth(this.activeElement.getWidth() + xchange)
        this.activeElement.setHeight(this.activeElement.getHeight() + ychange)
        break
    }
    if (this.activeElement.getWidth() == 0) {
      this.activeElement.setWidth(-1)
      this.activeElement.x += 1
    }
    if (this.activeElement.getHeight() == 0) {
      this.activeElement.setHeight(-1)
      this.activeElement.y += 1
    }
    if (this.activeElement.width < 0) {
      this.activeElement.x = this.activeElement.x + this.activeElement.getWidth()
      this.activeElement.setWidth(parseInt(this.activeElement.getWidth() * -1))
      this.swapActionHorizontal()
    }
    if (this.activeElement.height < 0) {
      this.activeElement.y = this.activeElement.y + this.activeElement.getHeight()
      this.activeElement.setHeight(this.activeElement.getHeight() * -1)
      this.swapActionVertical()
    }
    this.update()
  }

  // 拖动边框时上下反转
  this.swapActionVertical = function () {
    switch (this.dragAction) {
      case 1:
        this.dragAction = 6
        break
      case 2:
        this.dragAction = 7
        break
      case 3:
        this.dragAction = 8
        break
      case 6:
        this.dragAction = 1
        break
      case 7:
        this.dragAction = 2
        break
      case 8:
        this.dragAction = 3
        break
    }
  }

  // 拖动边框时左右反转
  this.swapActionHorizontal = function () {
    switch (this.dragAction) {
      case 1:
        this.dragAction = 3
        break
      case 3:
        this.dragAction = 1
        break
      case 4:
        this.dragAction = 5
        break
      case 5:
        this.dragAction = 4
        break
      case 6:
        this.dragAction = 8
        break
      case 8:
        this.dragAction = 6
        break
    }
  }

  //更新右侧属性
  this.update = function () {
    this.propertyInspector.update(this.activeElement)
  }

  this.updateCanvas = function (text) {
    //填充白色背景
    this.drawingContext.fillStyle = '#FFFFFF'
    this.drawingContext.fillRect(0, 0, this.canvas.width, this.canvas.height)

    this.drawingContext.lineWidth = 2 / this.labelScale

    //画笔颜色设置黑色
    this.drawingContext.strokeStyle = '#000000'
    this.drawingContext.fillStyle = '#000000'

    // 排序处理
    if (text && this.activeElement) {
      let k = this.elements.findIndex((i) => i == this.activeElement)
      let item = this.elements[k]
      this.elements.splice(k, 1)
      if (text == 'ZindexTop') {
        this.elements.push(item)
      } else if (text == 'ZindexBottom') {
        this.elements.unshift(item)
      } else if (text == 'ZindexUp') {
        let ki = k + 1

        this.elements.splice(ki, 0, item)
      } else if (text == 'ZindexDown') {
        let ki = k - 1
        if (ki < 0) {
          ki = 0
        }
        this.elements.splice(ki, 0, item)
      }
    }

    //绘制元素
    for (var i = 0; i < this.currentLayer; i++) {
      if (this.elements[i]) {
        // this.elements[i].draw(this.drawingContext, this.canvas.scale, this.canvas.width, this.canvas.height)
        this.elements[i].draw(this.drawingContext, this.canvas.scale, this.canvas.width, this.canvas.height)
      }
    }

    //绘制当前激活元素虚线框
    if (this.activeElement) {
      this.drawingContext.strokeStyle = '#0000FF'
      this.drawingContext.lineCap = 'butt'
      // this.drawingContext.name = this.activeElement.name
      this.activeElement.drawActive(this.drawingContext)
    }

    //更新右侧属性
    this.update()
  }

  this.generateZPL = function () {
    var data = '^XA^CW1,E:SIMSUN.TTF^CI28^MUm^PON'
    var bufferData = ''
    for (var i = 0; i < this.currentLayer; i++) {
      if (this.elements[i]) {
        bufferData += this.elements[i].toZPL(this.labelHeight, this.labelWidth)
      }
    }
    data += bufferData
    data += '^XZ'
    return data
  }

  // 定义左侧图例
  this.setNewObject = function (controller) {
    if (controller) {
      this.newObject = controller.object
      this.newObjectController = controller
    } else {
      this.newObject = null
      this.newObjectController = null
    }
  }

  this.addRectangle = function (x, y, width, height) {
    this.elements[this.currentLayer++] = new this.Rectangle(x, y, width, height)
    this.updateCanvas()
  }

  this.updateLabelSize(width, height, scale)

  this.updateCanvas()
}
